﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Forms;

using Ini;

namespace League_Window_Repositioner
{
    public partial class frmMain : Form
    {
        #region Dll Calls
        [DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)]
        static extern bool IsWindow(IntPtr hWnd);

        [DllImport("user32.dll", EntryPoint="FindWindow", SetLastError = true)]
        static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);

        [DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int width, int height, SetWindowPosFlags uFlags);

        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
        static readonly IntPtr HWND_TOP = new IntPtr(0);
        static readonly IntPtr HWND_BOTTOM = new IntPtr(1);

        [Flags]
        public enum SetWindowPosFlags : uint
        {
            // ReSharper disable InconsistentNaming

            /// <summary>
            ///     If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
            /// </summary>
            SWP_ASYNCWINDOWPOS = 0x4000,

            /// <summary>
            ///     Prevents generation of the WM_SYNCPAINT message.
            /// </summary>
            SWP_DEFERERASE = 0x2000,

            /// <summary>
            ///     Draws a frame (defined in the window's class description) around the window.
            /// </summary>
            SWP_DRAWFRAME = 0x0020,

            /// <summary>
            ///     Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.
            /// </summary>
            SWP_FRAMECHANGED = 0x0020,

            /// <summary>
            ///     Hides the window.
            /// </summary>
            SWP_HIDEWINDOW = 0x0080,

            /// <summary>
            ///     Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter).
            /// </summary>
            SWP_NOACTIVATE = 0x0010,

            /// <summary>
            ///     Discards the entire contents of the client area. If this flag is not specified, the valid contents of the client area are saved and copied back into the client area after the window is sized or repositioned.
            /// </summary>
            SWP_NOCOPYBITS = 0x0100,

            /// <summary>
            ///     Retains the current position (ignores X and Y parameters).
            /// </summary>
            SWP_NOMOVE = 0x0002,

            /// <summary>
            ///     Does not change the owner window's position in the Z order.
            /// </summary>
            SWP_NOOWNERZORDER = 0x0200,

            /// <summary>
            ///     Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of the window being moved. When this flag is set, the application must explicitly invalidate or redraw any parts of the window and parent window that need redrawing.
            /// </summary>
            SWP_NOREDRAW = 0x0008,

            /// <summary>
            ///     Same as the SWP_NOOWNERZORDER flag.
            /// </summary>
            SWP_NOREPOSITION = 0x0200,

            /// <summary>
            ///     Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
            /// </summary>
            SWP_NOSENDCHANGING = 0x0400,

            /// <summary>
            ///     Retains the current size (ignores the cx and cy parameters).
            /// </summary>
            SWP_NOSIZE = 0x0001,

            /// <summary>
            ///     Retains the current Z order (ignores the hWndInsertAfter parameter).
            /// </summary>
            SWP_NOZORDER = 0x0004,

            /// <summary>
            ///     Displays the window.
            /// </summary>
            SWP_SHOWWINDOW = 0x0040,

            // ReSharper restore InconsistentNaming
        }

        [DllImport("user32.dll")][return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;        // x position of upper-left corner
            public int Top;         // y position of upper-left corner
            public int Right;       // x position of lower-right corner
            public int Bottom;      // y position of lower-right corner
        }
        #endregion

        IntPtr lolHWND = IntPtr.Zero;
        Boolean seeWindow = false;
        IniFile config = new IniFile( Path.GetDirectoryName(Application.ExecutablePath) + "\\Location.ini" );
        int posX = 0;
        int posY = 0;

        public frmMain()
        {
            InitializeComponent();
            InitializeINI();
            bgwWindowFinder.RunWorkerAsync();
        }

        #region Background Worker
        private void bgwWindowFinder_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            while ( true ) {
                if( IsWindow( lolHWND ) ) {
                    worker.ReportProgress( 1 );
                } else {
                    worker.ReportProgress( 0 );
                    lolHWND = FindWindowByCaption( IntPtr.Zero, "League of Legends (TM) Client" );
                }
                Thread.Sleep( 2000 );
            }
        }

        private void bgwWindowFinder_ProgressChanged(object sender, ProgressChangedEventArgs e) {
            int result = e.ProgressPercentage;
            switch ( result ) 
            {
                case 0:
                    if ( seeWindow ) {
                        seeWindow = false;
                    }
                    break;
                case 1:
                    if ( !seeWindow ) {
                        seeWindow = true;
                    } else {
                        moveWindow();
                    }
                    break;
                default:
                    break;
            }
        }
        #endregion

        private void edtX_TextChanged(object sender, EventArgs e)
        {
            if ( IsDigitsOnly( edtX.Text ) ) {
                config.IniWriteValue( "Location", "X", edtX.Text.ToString() );
                moveWindow();
            } else {
                //MessageBox.Show( "Only put in numbers! Fix it!", "Nab!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation );
                edtX.Text = edtX.Text.Substring( 0, edtX.Text.Length - 1 );
            }
        }

        private void edtY_TextChanged(object sender, EventArgs e)
        {
            if ( IsDigitsOnly( edtY.Text ) ) {
                config.IniWriteValue( "Location", "Y", edtY.Text.ToString() );
                moveWindow();
            } else {
                //MessageBox.Show( "Only put in numbers! Fix it!", "Nab!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation );
                edtY.Text = edtY.Text.Substring( 0, edtY.Text.Length - 1 );
            }
        }
        
        private void frmMain_Resize_1(object sender, EventArgs e)
        {
            if ( FormWindowState.Minimized == this.WindowState ) {
                TrayIcon.Visible = true;
                this.Hide();
            }
        }

        private void TrayIcon_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            this.Show();
            TrayIcon.Visible = false;
        }

        private void moveWindow() {
            if ( seeWindow ){
                RECT leagueWindow = getWindowLocation( lolHWND );
                int width  = leagueWindow.Right - leagueWindow.Left;
                int height = leagueWindow.Bottom - leagueWindow.Top;
                int x;
                try { x = Convert.ToInt32( edtX.Text ); } catch { x = 0; }
                int y;
                try { y = Convert.ToInt32( edtY.Text ); } catch { y = 0; }
                //MessageBox.Show( (leagueWindow.Top == y).ToString() + " and " + (leagueWindow.Left == x).ToString() );
                //MessageBox.Show( "TOP: " + leagueWindow.Top + "Left: " + leagueWindow.Left + "X: " + x + "Y: " + y );
                if ( (leagueWindow.Top != y) || (leagueWindow.Left != x) ) {
                    //MessageBox.Show( "MOVEING WINDOW" );
                    SetWindowPos( lolHWND, HWND_TOP, x, y, width, height, SetWindowPosFlags.SWP_NOCOPYBITS | SetWindowPosFlags.SWP_SHOWWINDOW | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_ASYNCWINDOWPOS );
                }
            }
        }

        private RECT getWindowLocation( IntPtr HWND )
        {
            RECT rct = new RECT();
            if( !GetWindowRect( new HandleRef( this, HWND ), out rct ) ) {
                MessageBox.Show( "GetWindowRect had an error" );
            }
            return rct;
        }

        private void InitializeINI() {
            // X - Value
            string temp = config.IniReadValue("Location", "X");
            if ( temp != "" ) { 
                try { posX = Convert.ToInt32( temp ); } catch { posX = 0; }
            }
            edtX.Text = posX.ToString();

            // Y - Value
            temp = config.IniReadValue("Location", "Y");
            if ( temp != "" ) { 
                try { posY = Convert.ToInt32( temp ); } catch { posY = 0; }
            }
            edtY.Text = posY.ToString();
        }

        private bool IsDigitsOnly( string str ) {
            foreach ( char c in str ) {
                if ( c < '0' || c > '9' )
                    return false;
            }
            return true;
        }
    }
}
